import random
import string
from datetime import date

from src.course import Course
from src.students import Student
from utils import respond_code
from utils.format_str import print_table
from utils.db import load_json_data, save_json_data

# 常量命名字母大写
# 全局变量，在整个文件可调用
ADMIN = 'Yant'


class StudentManager:
    """学生管理模块，实现与管理学生行为、属性相关的逻辑"""
    prefix = 'YA'  # 学号前缀
    students_db = 'students.json'  # 数据存储文件

    def __init__(self, data_path):
        """
        初始化

        Parameters:
        data_path (str): 存储学生信息的 JSON 文件路径
        """
        self.__data_path = data_path
        self.__students = self.__load_student(self.__data_path)

    def add_student(self, name, age):
        """添加学生"""
        while True:
            # 检查学号是否存在，预防生成的学号重复的情况
            st_id = self.__generate_student_id
            if st_id not in self.__students:
                break
        # 添加学生，数据先存在内存
        student = Student(name, age, st_id)
        self.__students.update({student.student_id: student})
        # 保存到文件
        self.__save_student(self.__data_path)

    def get_student(self, student_id: str):
        """
        查询学生信息

        Parameters:
        student_id (str): 要查询的学生的学号
        name (str): 要查询的学生的姓名

        Returns:
        Union[Student, int]: 学生对象或查询学生信息异常代码。如果找到了学生，返回 Student 对象，如果没有找到，返回 QueryStudentException 异常代码。
        """
        # 这里的处理和课程不一样，因为保存学生的时候只保证了id是唯一的
        student: Student = self.__students.get(student_id, None)
        return student or respond_code.QueryStudentException(student_id).get_code

    def average_score(self, student_id):
        """查询平均成
        Parameters:
        student (Student): 学生对象
        """

        student = self.__students.get(student_id)
        if not isinstance(student, Student):
            return student
        # 检查学生是否有成绩
        if not student.scores:
            return respond_code.QueryScoreException(student.name).get_code
        count = len(student.scores)
        total = sum(student.scores.values())
        return total / count, total

    def query_curses(self, student_id, course_db):
        """
        查询学生课程

        Parameters:
        student (Student): 学生对象
        course_db (CourseManager): 课程管理器

        Returns:
        Union[List[Dict[str, Union[str, int]]], int]: 课程列表或查询课程异常代码
        """
        student = self.__students.get(student_id)
        if not isinstance(student, Student):
            print(student)
            return student

        if not student.courses:
            print(f" {student_id} 未选任何课程！")
            return respond_code.QueryStudentCourseException(student.name).get_code
        courses_list = []
        # 如果学生选择的课程有成绩，则补充成绩信息
        for course_id in student.courses:
            course = course_db.coursers_db[course_id]
            if course_id in student.scores:
                course['scores'] = student.scores[course_id]
            else:
                course['scores'] = 0
            courses_list.append(course)
        print('课程列表>>', courses_list)
        print_table(courses_list, headers=['课程名称', '课程id', '学分', '成绩'])

    @classmethod
    def select_course(cls, student: Student, course: Course):
        """选课"""
        if course.code not in student.courses:
            student.courses.append(course.code)
            print(f"选课成功！")
        else:
            print("您已选该课程！")

    @property
    def __generate_student_id(self):
        """生成学生id"""
        return ''.join([self.prefix, date.today().strftime('%Y%m%d'), ''.join(random.choices(string.digits, k=5))])

    @classmethod
    def __load_student(cls, file_path) -> dict[str, Student]:
        """
        加载学生
        """
        student_list_json = load_json_data(file_path)
        if not student_list_json:
            return {}
        # 反序序列化json 格式数据为 Student 对象
        return {student['student_id']: Student(**student) for student in student_list_json}

    def __save_student(self, file_path) -> None:
        """
        保存学生
        """
        # {student_id: student obj, ....} --》 [{"name":"Yant","age": 28,....},.....]
        students_json = [student.to_dict for student in self.__students.values()]
        save_json_data(filename=file_path, data=students_json)


class CourseManager:
    """课程管理模块，实现与管理课程相关的操作"""
    prefix = 'CYA'
    coursers_db = 'courses.json'

    def __init__(self, data_path):
        self.__data_path = data_path
        self.__courses = self.__load_courses()

    def query_course(self, course_id=None, name=None):
        """查询课程，支持通过id或name或id和name 查询
            分析：
                由于存储的数据特证，id和name 都是 唯一
                可能的情况
                1. 有id，那么数据最多有 1 条
                2. 有 id，有 name，优先根据id查询，那么数据最多有 1 条，如果id正确，name是错误， 0 条
                3. 有name，那么数据最多有 1 条


        """
        # 按 id 查询
        courses_list = []
        if course_id:
            course = self.__courses.get(course_id, None)
            if not name and course:
                courses_list.append(course.to_dict.values())
            if course and name:
                if course.course_name == name:
                    courses_list.append(course)
        # 按 name 查询
        elif name:
            for course in self.__courses.values():
                if course.course_name == name:
                    courses_list.append(course.to_dict.values())
        # 打印输出课程
        if not courses_list:
            print(f"没有查询到课程，请检查课程名称是否正确或课程是否存在，如有问题请联系管理员 {ADMIN} !")
            return
        print_table(courses_list, ['课程名称', '课程id', '学分'])

    def show_courses(self):
        if not self.__courses:
            print(f"当前学校还没有开设课程，请联系管理员 {ADMIN} !")
            return
        # 格式化输出课程信息
        # 方案1：
        # for... else ... 知识点
        # for course in self.__courses.values():
        # 动态计算对齐宽度
        # 生成表格
        # print(f"{'课程名称':>4} || {'课程id':7} || {'学分':3}")
        # for item in self.__courses.values():
        #     print(f"{item.course_name:>4} || {item.code:7} || {item.credit:3} ")
        #
        #     # print(f"课程代码：{course.code}  课程名称：{course.course_name} 学分：{course.credit}")
        # else:
        #     print()
        #     print(F'以前是当前所有可选课程，如没有你想要的课程联系管理员 {ADMIN} !!!')
        # 方案2:
        course_list = [course.to_dict.values() for course in self.__courses.values()]
        print_table(course_list, ['课程名称', '课程id', '学分'])

    def add_course(self, course_name: str, credit: int):
        """添加课程，id 和 name 都是唯一不重复"""
        while True:
            # 课程编号重复，则重新生成
            course_code = self.__course_id
            if course_code not in self.__courses:
                break
        # 因为数据结构是{code: course obj}, 处理名字就有点麻烦，这里使用 any 这个知识点
        if any(course.course_name.lower() == course_name.lower() for course in self.__courses.values()):
            return respond_code.CourseExistException(course_name).get_code
        # 更新课程信息
        self.__courses.update({course_code: Course(course_code, course_name, credit)})
        # 保存到文件
        self.__save_courses()
        return respond_code.AddCourseSuccess(course_name).get_code

    def drop_course(self, course_id: str):
        """删除课程"""
        if course_id not in self.__courses:
            return respond_code.CourseNotExistException(course_id).get_code
        course = self.__courses.pop(course_id)
        self.__save_courses()
        return respond_code.DropCourseSuccess(course.course_name).get_code

    def __load_courses(self) -> dict[str, Course]:
        """ 加载课程 """
        coursers_data = load_json_data(self.__data_path)
        return {course['code']: Course(**course) for course in coursers_data}

    def __save_courses(self):
        """ 保存课程，处理和保存 """
        courses_json = [course.to_dict for course in self.__courses.values()]
        save_json_data(filename=self.__data_path, data=courses_json)

    @property
    def __course_id(self):
        return ''.join([self.prefix, ''.join(random.choices(string.digits, k=5))])

    @property
    def courses(self):
        return self.__courses